package com.mdd.usercenter.service.impl;

import cn.hutool.core.date.DateTime;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mdd.usercenter.common.ErrorCode;
import com.mdd.usercenter.exception.BusinessException;
import com.mdd.usercenter.model.domain.Team;
import com.mdd.usercenter.model.domain.User;
import com.mdd.usercenter.model.domain.UserTeam;
import com.mdd.usercenter.model.enums.TeamStatusEnum;
import com.mdd.usercenter.model.request.TeamJoinRequest;
import com.mdd.usercenter.model.request.TeamQueryRequest;
import com.mdd.usercenter.model.request.TeamQuitRequest;
import com.mdd.usercenter.model.request.TeamUpdateRequest;
import com.mdd.usercenter.model.vo.TeamUserVo;
import com.mdd.usercenter.model.vo.UserVo;
import com.mdd.usercenter.service.TeamService;
import com.mdd.usercenter.mapper.TeamMapper;
import com.mdd.usercenter.service.UserService;
import com.mdd.usercenter.service.UserTeamService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
* @author mdd
* @description 针对表【team(队伍)】的数据库操作Service实现
* @createDate 2023-04-19 15:46:49
*/
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team>
    implements TeamService{


    @Autowired
    private UserTeamService userTeamService;

    @Autowired
    private UserService userService;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long addTeam(Team team, User loginUser) {
        //1.判断参数为空
        if (team==null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        //2.判断是否登录
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        //3.校验信息
        //  3.1队伍人数>1 && <=20
        int maxNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
        if (maxNum <= 1 || maxNum > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍人数有误");
        }
        //  3.2队伍标题<=20
        String name = team.getName();
        if (name.length() > 20 || StringUtils.isBlank(name)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍标题输入有误");
        }
        //  3.3队伍描述<=50
        String description = team.getDescription();
        if (description != null && description.length() > 50) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍描述过长");
        }
        //  3.4队伍状态 0为空开（默认） 1 为私密 2为密码
        Integer integer = Optional.ofNullable(team.getStatus()).orElse(0);
        TeamStatusEnum teamStatus = TeamStatusEnum.getTeamStatusByValues(team.getStatus());
        if (teamStatus == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍状态有误");
        }
        //  3.5密码（密码房）密码小于8位
        if (TeamStatusEnum.SECRET.equals(teamStatus)) {
            String password = team.getPassword();
            if (password.length() > 8) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码过长");
            } else if (password.length() == 0) {
                throw  new BusinessException(ErrorCode.PARAMS_ERROR,"密码不能为空");
            }
        }
        //  3.6超时时间
        Date expireTime = team.getExpireTime();
        if (expireTime.before(DateTime.now())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"超时时间有误");
        }
        //  3.7校验当前用户创建的队伍数，一个用户最多创建五个
        //todo bug：可能同时创建100个
        LambdaQueryWrapper<Team> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Team::getUserId,loginUser.getId());
        long count = count(queryWrapper);
        if (count >= 5) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"已超出最大创建队伍数");
        }
        //4.添加队伍
        team.setUserId(loginUser.getId());
        if (!save(team)) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"添加失败");
        }
        //5.添加用户-队伍关系表信息
        UserTeam userTeam = new UserTeam();
        userTeam.setTeamId(team.getId());
        userTeam.setUserId(loginUser.getId());
        userTeam.setJoinTime(new Date());
        userTeamService.save(userTeam);
        return team.getId();
    }

    @Override
    public List<TeamUserVo> getTeamList(TeamQueryRequest teamQueryRequest, boolean isAdmin, User loginUser) {
        LambdaQueryWrapper<Team> queryWrapper = new LambdaQueryWrapper<>();
        //是否为条件查询
        if (teamQueryRequest != null) {
            Long userId = teamQueryRequest.getUserId();
            String name = teamQueryRequest.getName();
            Integer maxNum = teamQueryRequest.getMaxNum();
            Integer status = teamQueryRequest.getStatus();
            String description = teamQueryRequest.getDescription();
            String searchText = teamQueryRequest.getSearchText();
            List<Long> idList = teamQueryRequest.getIdList();
            queryWrapper.eq(userId!=null,Team::getUserId,userId)
                    .like(StringUtils.isNotBlank(name),Team::getName,name)
                    .and(searchText!=null,qw -> qw.like(searchText!=null,Team::getName,searchText).or().like(searchText!=null,Team::getDescription,searchText))
                    .eq(maxNum!=null,Team::getMaxNum,maxNum)
                    .eq(status!=null&&status>=0,Team::getStatus,status)
                    .like(StringUtils.isNotBlank(description),Team::getDescription,description)
                    //是否超过过期时间
                    .and(qw -> qw.ge(Team::getExpireTime,new Date()).or().isNull(Team::getExpireTime))
                    .in(idList!=null,Team::getId,idList);
        }

        //管理员和当前用户可以查看私密队伍
        Integer status = Optional.ofNullable(teamQueryRequest.getStatus()).orElse(0);
        if (!isAdmin&&status==1) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        //公共队列中显示当前用户的私密队列，list中加入当前用户创建的私有队列
        ArrayList<Team> list = new ArrayList<>();
        if (status == 0) {
            LambdaQueryWrapper<Team> queryWrapper1 = new LambdaQueryWrapper<>();
            queryWrapper1.eq(Team::getUserId,loginUser.getId()).eq(Team::getStatus,1);
            list.addAll(list(queryWrapper1));
        }
        //查询
        List<Team> list1 = list(queryWrapper);
        list.addAll(list1);
        if (list.isEmpty()) {
            return new ArrayList<>();
        }
        //查询每队创建人信息
        ArrayList<TeamUserVo> teamUserVos = new ArrayList<>();
        for (Team team : list) {
            //查询创建人信息
            Long userId = team.getUserId();
            User user = userService.getById(userId);
            //拷贝队伍信息到封装类
            TeamUserVo teamUserVo = new TeamUserVo();
            BeanUtils.copyProperties(team,teamUserVo);
            //拷贝创建人信息到封装类
            UserVo userVo = new UserVo();
            BeanUtils.copyProperties(user,userVo);
            teamUserVo.setUser(userVo);

            teamUserVos.add(teamUserVo);
        }
        return teamUserVos;
    }

    @Override
    public boolean updateTeam(TeamUpdateRequest teamUpdateRequest, User loginUser) {
        //校验
        if (teamUpdateRequest == null) {
            throw  new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //查看当前队伍是否存在
        Team team = getById(teamUpdateRequest.getId());
        if (team == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR,"队伍不存在！");
        }
        //检验只能管理员或者队长可以修改
        if (!team.getUserId().equals(loginUser.getId()) && loginUser.getUserRole() != 1) {
            throw new BusinessException(ErrorCode.NO_AUTH,"该用户没有修改权限");
        }
        //如果要设置队伍为加密必须传入密码
        TeamStatusEnum status = TeamStatusEnum.getTeamStatusByValues(teamUpdateRequest.getStatus());
        if (status.equals(TeamStatusEnum.SECRET) && teamUpdateRequest.getPassword() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"请输入密码");
        }
        //修改
        Team team1 = new Team();
        BeanUtils.copyProperties(teamUpdateRequest,team1);
        return updateById(team1);
    }

    /**
     * 加入队伍
     * @param teamJoinRequest
     * @param loginUser
     * @return
     */
    @Override
    public Boolean joinTeam(TeamJoinRequest teamJoinRequest, User loginUser) {
        //校验
        if (teamJoinRequest == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        //判断队伍是否存在，只能加入未过期公开的队伍
        Long id = teamJoinRequest.getTeamId();
        if (id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍id有误");
        }
        Team team = getById(id);
        //是否存在
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"此队伍不存在!");
        }
        TeamStatusEnum status = TeamStatusEnum.getTeamStatusByValues(team.getStatus());
        //判断是否可以加入
        if (status.equals(TeamStatusEnum.PRIVATE)||!team.getExpireTime().after(new Date())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"无法加入该队伍!");
        }
        //如果是密码队伍判断加入密码是否正确
        if (status.equals(TeamStatusEnum.SECRET) && !teamJoinRequest.getPassword().equals(team.getPassword())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码错误!");
        }

        //加锁保障幂等性
        RLock lock = redissonClient.getLock("lock:tempJoin");
        boolean res = false;
        try {
            int n = 0;
            while (true) {
                if (lock.tryLock(0, -1, TimeUnit.SECONDS)) {
                    //校验当前用户加入的队伍数
                    LambdaQueryWrapper<UserTeam> queryWrapper = new LambdaQueryWrapper<>();
                    queryWrapper.eq(UserTeam::getUserId, loginUser.getId());
                    long count = userTeamService.count(queryWrapper);
                    if (count > 5) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "每个用户最多加入或创建五个队伍");
                    }
                    //判断队伍是否人满
                    Long members = getTeamNum(team);
                    if (members >= team.getMaxNum()) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已满!");
                    }
                    //判断是否已加入队列
                    LambdaQueryWrapper<UserTeam> queryWrapper2 = new LambdaQueryWrapper<>();
                    queryWrapper2.eq(UserTeam::getTeamId, team.getId()).eq(UserTeam::getUserId, loginUser.getId());
                    long isJoin = userTeamService.count(queryWrapper2);
                    if (isJoin != 0) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "不能重复加入");
                    }
                    //新增队伍-用户关联信息
                    UserTeam userTeam = new UserTeam();
                    userTeam.setTeamId(team.getId());
                    userTeam.setUserId(loginUser.getId());
                    userTeam.setJoinTime(new Date());
                    return userTeamService.save(userTeam);
                }else {
                    Thread.sleep(500);
                    n++;
                    if (n == 10) {
                        return false;
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            return false;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 离开队伍
     * @param teamQuitRequest
     * @param loginUser
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean quitTeam(TeamQuitRequest teamQuitRequest, User loginUser) {
        if (teamQuitRequest == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        //校验队伍是否存在
        Long teamId = teamQuitRequest.getTeamId();
        Team team = getTeamById(teamId);

        //校验是否加入队列
        Long userId = loginUser.getId();
        UserTeam userTeam = new UserTeam();
        userTeam.setTeamId(teamId);
        userTeam.setUserId(userId);
        LambdaQueryWrapper<UserTeam> queryWrapper = new LambdaQueryWrapper<>(userTeam);
        long count = userTeamService.count(queryWrapper);
        if (count != 1) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"未加入该队列");
        }
        //获取当前队伍的人数
        Long teamNum = getTeamNum(team);
        //如果队伍只剩一人
        if (teamNum == 1) {
            //删除用户队伍关系信息
            //删除队伍信息
            if (!removeById(teamId)||!userTeamService.remove(queryWrapper)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"删除失败");
            }
            return true;
        }
        //队伍有多人
        //判断当前用户是否为队长
        if (!loginUser.getId().equals(team.getUserId())) {
            //不是队长
            boolean remove = userTeamService.remove(queryWrapper);
            if (!remove) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"退出失败");
            }
            return remove;
        }
        //是队长
        //将队长转移给最早加入的用户
        //1.查询已加入队伍的所有用户
        LambdaQueryWrapper<UserTeam> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(UserTeam::getTeamId,teamId).last("order by id asc limit 2");
        List<UserTeam> list = userTeamService.list(queryWrapper1);
        UserTeam nextUser = list.get(1);
        //队长转移
        Long nextUserUserId = nextUser.getUserId();
        Team tempTeam = new Team();
        tempTeam.setUserId(nextUserUserId);
        tempTeam.setId(teamId);
        boolean b = updateById(tempTeam);
        if (!b) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"更新队长失败");
        }
        //删除用户队伍信息
        return userTeamService.remove(queryWrapper);
    }

    /**
     * 解散队伍
     * @param teamId
     * @param loginUser
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean disbandTeam(Long teamId, User loginUser) {
        //校验队伍是否存在
        Team team = getTeamById(teamId);
        //校验当前用户是否为队长
        if (!loginUser.getId().equals(team.getUserId())) {
            throw new BusinessException(ErrorCode.NO_AUTH,"只有队长可解散");
        }
        //移除队伍关联信息
        LambdaQueryWrapper<UserTeam> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserTeam::getTeamId,teamId);
        boolean remove = userTeamService.remove(queryWrapper);
        //移除队伍信息
        boolean b = removeById(teamId);
        if (!b&&remove) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"解散队伍失败");
        }
        return true;
    }

    /**
     * 根据id获取队伍信息
     * @param teamId
     * @return
     */
    private Team getTeamById(Long teamId){
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //校验队伍是否存在
        Team team = getById(teamId);
        if (team == null) {
            throw  new BusinessException(ErrorCode.PARAMS_ERROR,"队伍不存在");
        }
        return team;
    }

    /**
     * 获取当前前队伍的人数
     * @param team
     * @return
     */
    private Long getTeamNum(Team team){
        LambdaQueryWrapper<UserTeam> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(UserTeam::getTeamId,team.getId());
        return userTeamService.count(queryWrapper1);
    }
}




